package com.fingard.client;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.Socket;

import com.fingard.FGBiz;
import com.fingard.diagnostics.LogHelper;
import com.fingard.net.ConnState;
import com.fingard.net.TcpReceiver;
import com.fingard.text.StringHelper;
import com.fingard.text.UrlHelper;

public class DspClient {
	
	private String ip;
	private int port;
	public DspClient(String p_ip, int p_port){
		ip = p_ip;
		port = p_port;
		if(StringHelper.hasAnyChar(FGBiz.dspCenter.dspSet.encodingWithDspClient)){
			charsetName = FGBiz.dspCenter.dspSet.encodingWithDspClient;
		}
	}
	public DspClient(String p_ipAndPort){
		String[] ipt = UrlHelper.pickupIpAndPort(p_ipAndPort);
		ip=ipt[0];
		port = Integer.parseInt(ipt[1]);
		if(StringHelper.hasAnyChar(FGBiz.dspCenter.dspSet.encodingWithDspClient)){
			charsetName = FGBiz.dspCenter.dspSet.encodingWithDspClient;
		}
	}
	
	public void setCharset(String pCharset){
		charsetName = pCharset;
	}
	
	Socket socket = null;
	
	/**超时时间，单位：秒，默认10秒*/
	public int timeoutSec = 10;
	
	public TcpReceiver getFile(String p_fileType, String p_remotePath) throws UnsupportedEncodingException{
		TcpReceiver retReceiver = new TcpReceiver();
		retReceiver.charsetName = charsetName;
		String tmpSendHead = "filepath:"+p_remotePath+"\r\nfiletype:"+p_fileType+"\r\naction:getfile\r\n\r\n";
		byte[] tmpBytesHead = tmpSendHead.getBytes(charsetName);
		try {
			socket = new Socket(ip, port);
			socket.setSoTimeout(timeoutSec*1000);
		}
		catch (Exception e) {
			retReceiver.connState = ConnState.FAIL;
			retReceiver.connMsg = e.getMessage();
			return retReceiver;
		}
		
		OutputStream tmpOutput = null;
		InputStream tmpInput = null;
		try{
			tmpOutput = socket.getOutputStream();
			
			tmpOutput.write(tmpBytesHead, 0, tmpBytesHead.length);
			tmpInput = socket.getInputStream();
			
			receiveFromInputStream(retReceiver, tmpInput);
			
			tmpOutput.close();
			tmpOutput = null;
			tmpInput.close();
			tmpInput = null;
			
		}catch (Throwable e) {
			e.printStackTrace();
			retReceiver.connMsg += LogHelper.getStackTrace(e);
		}finally{
			if(tmpOutput!=null){
				try{
					tmpOutput.close();
				}catch (IOException e) {
					e.printStackTrace();
					retReceiver.connMsg += LogHelper.getStackTrace(e);
				}
			}
			if(tmpInput!=null){
				try{
					tmpInput.close();
				}catch (IOException e) {
					e.printStackTrace();
					retReceiver.connMsg += LogHelper.getStackTrace(e);
				}
			}
			if(socket!=null){
				try{
					socket.close();
				}catch (IOException e) {
					e.printStackTrace();
					retReceiver.connMsg += LogHelper.getStackTrace(e);
				}
			}
		}
		
		return retReceiver;
	}
	
	/**将文件内容推送到远程DSP*/
	public TcpReceiver putFile(String p_content, String p_fileType, String p_remotePath, boolean p_append) throws UnsupportedEncodingException{
		String headAppend = "false";
		if(p_append){
			headAppend = "true";
		}
		String tmpSendHead = "filepath:"+p_remotePath+"\r\nfiletype:"+p_fileType+"\r\nappend:"+headAppend;
		return syncFileContent(p_content, tmpSendHead,"syncfile-length:");
	}
	
	public TcpReceiver saveToDb(String p_content) throws UnsupportedEncodingException{
		String tmpSendHead = "action:savetodb";
		return syncFileContent(p_content, tmpSendHead, "content-length:");
	}
	
	public TcpReceiver sendMsg(String pContent) throws UnsupportedEncodingException{
		return syncFileContent(pContent, "", "content-length:");
	}
	
	public TcpReceiver syncFileContent(String p_content, String pHeader, String pActionLen) throws UnsupportedEncodingException{
		TcpReceiver retReceiver = new TcpReceiver();
		retReceiver.charsetName = charsetName;
		
		try {
			socket = new Socket(ip, port);
			socket.setSoTimeout(timeoutSec*1000);
		}
		catch (Exception e) {
			retReceiver.connState = ConnState.FAIL;
			retReceiver.connMsg = e.getMessage();
			return retReceiver;
		}

		OutputStream tmpOutput = null;
		InputStream tmpInput = null;
		try{
			byte[] bodyBytes = p_content.getBytes(charsetName);
			String tmpStrLen = String.format("% 8d", bodyBytes.length);
			
			String tmpSendHead = pActionLen + tmpStrLen + "\r\n" + pHeader+"\r\n\r\n";//"syncfile-length:" + tmpStrLen + "\r\nfilepath:"+p_remotePath+"\r\nfiletype:"+p_fileType+"\r\nappend:"+headAppend+"\r\n\r\n";
			byte[] tmpBytesHead = tmpSendHead.getBytes(charsetName);
			
			byte[] tmpBytesToSend = new byte[tmpBytesHead.length + bodyBytes.length];
			System.arraycopy(tmpBytesHead, 0, tmpBytesToSend, 0, tmpBytesHead.length);
	        System.arraycopy(bodyBytes, 0, tmpBytesToSend, tmpBytesHead.length, bodyBytes.length);
	        
			tmpOutput = socket.getOutputStream();
			
			tmpOutput.write(tmpBytesToSend, 0, tmpBytesToSend.length);
			tmpOutput.flush();
			tmpInput = socket.getInputStream();
			
			receiveFromInputStream(retReceiver, tmpInput);
			
			tmpOutput.close();
			tmpOutput = null;
			tmpInput.close();
			tmpInput = null;
			
		}catch (Throwable e) {
			e.printStackTrace();
			retReceiver.connMsg += LogHelper.getStackTrace(e);
		}finally{
			if(tmpOutput!=null){
				try{
					tmpOutput.close();
				}catch (IOException e) {
					e.printStackTrace();
					retReceiver.connMsg += LogHelper.getStackTrace(e);
				}
			}
			if(tmpInput!=null){
				try{
					tmpInput.close();
				}catch (IOException e) {
					e.printStackTrace();
					retReceiver.connMsg += LogHelper.getStackTrace(e);
				}
			}
			if(socket!=null){
				try{
					socket.close();
				}catch (IOException e) {
					e.printStackTrace();
					retReceiver.connMsg += LogHelper.getStackTrace(e);
				}
			}
		}
		return retReceiver;
	}
	
	/**将文件同步至远程DSP*/
	public TcpReceiver syncFile(String p_localFilePath, String p_fileType, String p_bankCode,String pSyBank, String p_remotePath) throws UnsupportedEncodingException{
		TcpReceiver retReceiver = new TcpReceiver();
		retReceiver.charsetName = charsetName;
		File tmpFile = new File(p_localFilePath);
		if(!tmpFile.exists()){
			retReceiver.connMsg = ConnState.FILE_NOT_EXISTS;
			retReceiver.connMsg = "文件"+p_localFilePath+"不存在";
			return retReceiver;
		}
		
		Long fileLen = tmpFile.length();
		String tmpStrLen = String.format("% 8d", fileLen);
		String tmpSendHead = "syncfile-length:" + tmpStrLen + "\r\nfilepath:"+p_remotePath+"\r\nfiletype:"+p_fileType+"\r\nbankcode:"+p_bankCode+"\r\nsybank:"+pSyBank+"\r\n\r\n";
		byte[] tmpBytesHead = tmpSendHead.getBytes(charsetName);
		int bufferLen;
		if(fileLen>2048000){//2M
			bufferLen = 1024000;
		}else if(fileLen>512000){
			bufferLen = 512000;
		}else{
			bufferLen = fileLen.intValue() + tmpBytesHead.length;
		}
		
		try {
			socket = new Socket(ip, port);
			socket.setSoTimeout(timeoutSec*1000);
		}
		catch (Exception e) {
			retReceiver.connState = ConnState.FAIL;
			retReceiver.connMsg = e.getMessage();
			return retReceiver;
		}
		OutputStream tmpOutput = null;
		InputStream tmpInput = null;
		FileInputStream fileStream = null;
		try{
			tmpOutput = socket.getOutputStream();
			
			byte[] sendBuffer = new byte[bufferLen];
			System.arraycopy(tmpBytesHead, 0, sendBuffer, 0, tmpBytesHead.length);//复制头部至缓冲区
			int offSetSnd = tmpBytesHead.length;//第一次写有头部，需要标记
			
			fileStream = new FileInputStream(tmpFile);
			long sendedLen = 0;
			Long expectSndLen = fileLen+tmpBytesHead.length;
			while(sendedLen<expectSndLen){
				int readLen = fileStream.read(sendBuffer,offSetSnd,sendBuffer.length-offSetSnd);
				if(offSetSnd==0 && readLen==0){
					break;
				}
				tmpOutput.write(sendBuffer, 0, offSetSnd+readLen);
				sendedLen=sendedLen+offSetSnd+readLen;
				offSetSnd = 0;//头部发送后，整个缓冲区用于读
			}
			fileStream.close();
			fileStream = null;
			
			if(sendedLen!=expectSndLen){
				retReceiver.connState = ConnState.Internal_Server_Error;
				retReceiver.connMsg = "实际发送总字节数"+sendedLen+"与预期"+expectSndLen+"不相等（其中头部"+tmpBytesHead.length+"，文件体"+fileLen+"）";
				tmpOutput.close();
				tmpOutput = null;
				return retReceiver;
			}
			tmpOutput.flush();
			tmpInput = socket.getInputStream();
			
			receiveFromInputStream(retReceiver, tmpInput);
			
			tmpOutput.close();
			tmpOutput = null;
			tmpInput.close();
			tmpInput = null;
			
		}catch (Throwable e) {
			e.printStackTrace();
			retReceiver.connMsg += LogHelper.getStackTrace(e);
		}finally{
			if(tmpOutput!=null){
				try{
					tmpOutput.close();
				}catch (IOException e) {
					e.printStackTrace();
					retReceiver.connMsg += LogHelper.getStackTrace(e);
				}
			}
			if(tmpInput!=null){
				try{
					tmpInput.close();
				}catch (IOException e) {
					e.printStackTrace();
					retReceiver.connMsg += LogHelper.getStackTrace(e);
				}
			}
			if(fileStream!=null){
				try{
					fileStream.close();
				}catch (IOException e) {
					e.printStackTrace();
					retReceiver.connMsg += LogHelper.getStackTrace(e);
				}
			}
			if(socket!=null){
				try{
					socket.close();
				}catch (IOException e) {
					e.printStackTrace();
					retReceiver.connMsg += LogHelper.getStackTrace(e);
				}
			}
		}
		return retReceiver;
	}
	
	private void receiveFromInputStream(TcpReceiver retReceiver, InputStream tmpInput) throws Exception{
		retReceiver.recBytes = new byte[2048];//2K
		while(!retReceiver.isCompleted){
			if((retReceiver.recBytes.length - retReceiver.receiveOffSet)<=0){
				break;
			}
			int tmpReadLen = tmpInput.read(retReceiver.recBytes, retReceiver.receiveOffSet, retReceiver.recBytes.length - retReceiver.receiveOffSet);
			if(tmpReadLen<=0){
				break;
			}
			retReceiver.receiveOffSet += tmpReadLen;
			retReceiver.readedLength = retReceiver.readedLength + tmpReadLen;
			if(retReceiver.contentLength == -1){
				getContentLength(retReceiver);
			}
			if(StringHelper.hasAnyChar(retReceiver.connState)){
				break;
			}
			if (retReceiver.contentLength >= 0 && retReceiver.contentStartIndex >= 0){
                if (retReceiver.recBytes.length < retReceiver.contentLength + retReceiver.contentStartIndex){
                    byte[] tmpUnionInfo = new byte[retReceiver.contentLength + retReceiver.contentStartIndex];//一次性分配内存
    				System.arraycopy(retReceiver.recBytes, 0, tmpUnionInfo, 0, retReceiver.receiveOffSet);
    				retReceiver.recBytes = null;
    				retReceiver.recBytes = tmpUnionInfo;
                }
                
                int tmpToReadLen = retReceiver.contentLength + retReceiver.contentStartIndex - retReceiver.receiveOffSet;
                if (tmpToReadLen <= 0){
                	retReceiver.isCompleted = true;
                	break;
                }
            }
			/*
			else if(retReceiver.recBytes.length - retReceiver.receiveOffSet < 1024){//扩展
				byte[] tmpUnionInfo2 = new byte[retReceiver.receiveOffSet + 20480];
				System.arraycopy(retReceiver.recBytes, 0, tmpUnionInfo2, 0, retReceiver.receiveOffSet);
				retReceiver.recBytes = null;
				retReceiver.recBytes = tmpUnionInfo2;
			}
			*/
		}
	}
	
	public String charsetName = "GBK";
	
	private static byte tmpByteLt = '<';//"<".getBytes("gbk")[0];
    private static byte tmpHeadCut1 = '\r';//"\\r".getBytes("gbk")[0];
    private static byte tmpHeadCut2 = '\n';//"\\n".getBytes("gbk")[0];
    
	private void getContentLength(TcpReceiver p_receiver) throws Exception{
		
		if (p_receiver.contentLength == -1 || p_receiver.contentStartIndex == -1) {
            for (int i = p_receiver.readHeadStartIndex; i < p_receiver.readedLength; i++) {
            	if (p_receiver.recBytes[i] == tmpHeadCut2){// 是\n
            		
                    int tmpEndCount = i - p_receiver.readHeadStartIndex;
                    if (i > 1 && p_receiver.recBytes[i - 1] == tmpHeadCut1){// 前一个字符是\r
                    	tmpEndCount--;
                    }
                    String tmpHeadKeyValue = new String(p_receiver.recBytes, p_receiver.readHeadStartIndex, tmpEndCount, p_receiver.charsetName);
                    String tmpKey = "";
                    String tmpValue = "";

                    int idx1 = tmpHeadKeyValue.indexOf(":");
                    if(idx1>0){
                    	tmpKey = tmpHeadKeyValue.substring(0,idx1).toLowerCase();
                    	tmpValue = tmpHeadKeyValue.substring(idx1+1).toLowerCase();
                    }
                    
                    if (tmpKey.equals("content-length")){
                    	p_receiver.contentLength = Integer.valueOf(tmpValue.trim());
                    	if(p_receiver.firstCutLn<0){
                			p_receiver.firstCutLn = i;
                		}
                    	p_receiver.contentStartIndex = i + 1;
                    	break;
                    }else if (tmpKey.equals("cipher-length")){
                    	p_receiver.isCipherSocket = true;
                    	p_receiver.contentLength = Integer.valueOf(tmpValue.trim());
                    }else if (tmpKey.equals("syncfile-length")){
                    	p_receiver.action = "syncfile";
                    	p_receiver.isSyncFile = true;
                    	p_receiver.contentLength = Integer.valueOf(tmpValue.trim());
                    }else if (tmpKey.equals("action")){
                    	p_receiver.action = tmpValue;
                    }else if (tmpKey.equals("file-length")){
                    	p_receiver.isRtnBinaryFile=true;
                    	p_receiver.contentLength = Integer.valueOf(tmpValue.trim());
                    }else if (tmpKey.equals("filepath")){
                    	p_receiver.oriFilePath = tmpValue.replace("\\", File.separator).replace("/", File.separator);
                    }else if (tmpKey.equals("filetype")){
                    	p_receiver.fileType = tmpValue;
                    }else if (tmpKey.equals("charset")){
                    	p_receiver.charsetName = tmpValue;
                    }else if (tmpKey.equals("cipher-key")){
                    	p_receiver.cipherKey = tmpValue;
                    }else if (tmpKey.equals("cipher-iv")){
                    	p_receiver.cipherIV = tmpValue;
                    }else if (tmpKey.equals("digest-sign")){
                    	p_receiver.digestSign = tmpValue;
                    }
                    p_receiver.readHeadStartIndex = i + 1;
                    if (p_receiver.isCipherSocket || p_receiver.isSyncFile || p_receiver.isRtnBinaryFile){//如果是密文或者同步文件，以两个\r\n结束头部
                        if (p_receiver.receiveOffSet > (i + 2)){
                            if (p_receiver.recBytes[i + 1] == tmpHeadCut1 && p_receiver.recBytes[i + 2] == tmpHeadCut2){
                            	p_receiver.contentStartIndex = i + 3;
                                break;
                            }
                        }
                    }else if (p_receiver.receiveOffSet > (i + 1) && p_receiver.recBytes[i + 1] == tmpByteLt){
                    	p_receiver.contentStartIndex = i + 1;
                        break;
                    }
                }else if (p_receiver.recBytes[i] == tmpByteLt){// 是<
                	p_receiver.contentStartIndex = i;
                    break;
                }
            }
            if(p_receiver.contentLength>=0 && p_receiver.contentStartIndex<0){
            	if(!p_receiver.isCipherSocket && !p_receiver.isSyncFile){//普通报文
            		if(p_receiver.firstCutLn>0){
                		p_receiver.contentStartIndex = p_receiver.firstCutLn+1;
                	}else{
                		p_receiver.contentStartIndex = 0;
                	}
            	}
            }
            if (p_receiver.contentLength < 0 || p_receiver.contentStartIndex < 0){
                if (p_receiver.isCipherSocket == false && p_receiver.isSyncFile == false && p_receiver.receiveOffSet > 100){
                	p_receiver.connState= ConnState.Unknow_Protocol;
                	p_receiver.connMsg = "未知的通讯头部：" + new String(p_receiver.recBytes, 0, p_receiver.receiveOffSet, p_receiver.charsetName);
                }
            }
        }
	}
}
